/***************************************************************
 * Name:      QtHelper
 * Purpose:   Code::Blocks plugin
 * Author:    Sviridov Michael Alexandrovich (2mihail@mail.ru)
 * Created:   2008-09-20
 * Copyright: Sviridov Michael Alexandrovich
 * License:   GPL
 **************************************************************/
#include "QtPrjGen.h"
#include <sdk.h>
#include <wx/textfile.h>
#include "debug_msg.h"
//-------------------------------------------------
std::list<wxString>& QtPrjGen::str_arr2list(const wxArrayString &arr, std::list<wxString> &lst)
{
	for(unsigned i=0; i<arr.GetCount(); ++i)
		lst.push_back(arr[i]);

	return lst;
}
//-------------------------------------------------
std::list<wxString>& QtPrjGen::get_values(const wxString &nm)
{
	return config[nm];
}
//-------------------------------------------------
void QtPrjGen::set_values(const wxString &nm, const std::list<wxString> &values)
{
	config[nm] = values;
}
//-------------------------------------------------
void QtPrjGen::add_value(const wxString &nm, const wxString &value)
{
	if(value.IsEmpty())
		return;

	config[nm].push_back(value);
}
//-------------------------------------------------
wxString& QtPrjGen::mk_quoted_str(wxString& str, bool force)
{
	if(!force)
		return str;

	if(compiler->GetSwitches().forceCompilerUseQuotes ||
		compiler->GetSwitches().forceLinkerUseQuotes ||
		(str.Find(' ') != -1 && str.GetChar(0) != '"'))
		str = _T('"') + str + _T('"');

	return str;
}
//-------------------------------------------------
/*std::map<wxString, wxString> macroNames;
/// Find, extract and expand macroses (environment variables)
wxString QtPrjGen::extractGlobalsIfAny(const wxString &str)
{
	wxRegEx regEx;
	regEx.Compile("$(#*)*");
	if(regEx)

	if(!str.Matches("$(#*)*"))
		return str;

	int pos1 = str.First(_T("$(#"));
	if(pos1 == -1)
		return str;

	int pos2 = str[pos1 + 3].Find(')');
	if(pos2 == -1)
		return str;

	wxString macroName = str.Mid(pos1 + 3, pos2 - pos1 - 3);
	std::map<wxString, wxString>::iterator iter = macroNames.find(macroName);
	if(iter != macroNames.end())
		return str;

	wxString &nodeValue = macroNames[macroName];
	nodeValue = _T("$(#") + macroName + _T(")");
	Manager::Get()->GetMacrosManager()->ReplaceEnvVars(nodeValue);

	return _T("$$") + macroName + str.Mid(pos2 + 1);
}*/
//-----------------------------------------------------------
bool QtPrjGen::create_pro(cbProject *prj, const wxString &fn)
{
	config.clear();
	if(!prj)
		return false;

	project = prj;
	if(!check_target())
		return false;
	if(!check_has_files())
		return false;
	if(!check_compiler())
		return false;

	fill_config();
	fill_compiler_opts();
	fill_linker_opts();
	fill_libs();
	fill_lib_dirs();
	fill_include_dirs();
	fill_prj_files();
	bool ret = save2pro(fn);

	return ret;
}
//-------------------------------------------------
bool QtPrjGen::check_target()
{
	target = project->GetBuildTarget(project->GetActiveBuildTarget());
	if(target)
		return true;

	dbg_err(_T("QtHelper> current target not found"));
	return false;
}
//-------------------------------------------------
bool QtPrjGen::check_has_files()
{
	// FIXME why here function GetFilesCount() returns 0 when project has files
	int files_cnt = project->GetFilesCount();
	dbg_msg(wxString(_T("QtHelper> project files count = ")) << files_cnt);
	//checking for existence of files in current target
	bool has_files = false;
	for(int i=0; i<files_cnt; ++i)
	{
		ProjectFile* file = project->GetFile(i);
		if(/*file->link && */file->buildTargets.Index(target->GetTitle()) >= 0)
		{
			has_files = true;
			break;
		}
	}

	if(has_files)
		return true;

	dbg_err(_T("QtHelper> current target has no files for building"));
	return false;
}
//-------------------------------------------------
bool QtPrjGen::check_compiler()
{
	wxString compiler_id = target->GetCompilerID();
	compiler = compiler_id.IsEmpty()? CompilerFactory::GetDefaultCompiler() : CompilerFactory::GetCompiler(compiler_id);
	if(compiler)
		return true;

	dbg_err(_T("QtHelper> current compiler not found"));
	return false;
}
//-------------------------------------------------
bool QtPrjGen::save2pro(const wxString &fn)
{
	wxTextFile file(fn);
	if(file.Exists())
	{
		if(!file.Open(fn))
		{
			dbg_err(_T("QtHelper> can't open .pro-file for writing"));
			return false;
		}
	}
	else if(!file.Create(fn))
	{
		dbg_err(_T("QtHelper> can't create .pro-file for writing"));
		return false;
	}

	file.Clear();
	size_t line=0;
	for(VARS_LIST::iterator i = config.begin(); i != config.end(); ++i)
	{
		wxString str = i->first;

		std::list<wxString> &lst = i->second;
		for(std::list<wxString>::iterator j = lst.begin(); j != lst.end(); ++j)
			str << _T(" \\\n\t") << *j;

		file.InsertLine(str, line++);
	}

	TiXmlNode *prj_node = project->GetExtensionsNode();
	// adding entire project custom pro-file tags
	TiXmlElement *elem = prj_node->FirstChildElement("qt_helper");
	if(elem)
	{
		elem = elem->FirstChildElement("custom_tags");
		if(elem)
		{
			const char *text = elem->Attribute("value");
			if(text)
			{
				wxString custom_tags = cbC2U(text);
				Manager::Get()->GetMacrosManager()->ReplaceEnvVars(custom_tags);
				file.InsertLine(custom_tags, line++);
			}
		}
	}

	// adding current target custom pro-file tags
	elem = prj_node->FirstChildElement("qt_helper");
	if(elem)
	{
		elem = elem->FirstChildElement("targets");
		if(elem)
		{
			elem = elem->FirstChildElement(cbU2C(target->GetTitle()));
			if(elem)
			{
				elem = elem->FirstChildElement("custom_tags");
				if(elem)
				{
					const char *text = elem->Attribute("value");
					if(text)
					{
						wxString custom_tags = cbC2U(text);
						Manager::Get()->GetMacrosManager()->ReplaceEnvVars(custom_tags);
						file.InsertLine(custom_tags, line++);
					}
				}
			}
		}
	}

	if(!file.Write())
	{
		dbg_err(_T("QtHelper> can't save .pro-file"));
		return false;
	}

	config.clear();
	return true;
}
//-------------------------------------------------
void QtPrjGen::fill_compiler_opts()
{
	OptionsRelation relation = target->GetOptionRelation(ortCompilerOptions);

	std::list<wxString> prj_compiler_opts;
	str_arr2list(project->GetCompilerOptions(), prj_compiler_opts);
	std::list<wxString> target_compiler_opts;
	str_arr2list(target->GetCompilerOptions(), target_compiler_opts);

	switch(relation)
	{
		case orUseParentOptionsOnly:
			set_values(_T("QMAKE_CXXFLAGS +="), prj_compiler_opts);
			break;

		case orUseTargetOptionsOnly:
			set_values(_T("QMAKE_CXXFLAGS +="), target_compiler_opts);
			break;

		case orPrependToParentOptions:
			target_compiler_opts.merge(prj_compiler_opts);
			set_values(_T("QMAKE_CXXFLAGS +="), target_compiler_opts);
			break;

		case orAppendToParentOptions:
			prj_compiler_opts.merge(target_compiler_opts);
			set_values(_T("QMAKE_CXXFLAGS +="), prj_compiler_opts);
			break;
	}
}
//-------------------------------------------------
void QtPrjGen::fill_linker_opts()
{
	OptionsRelation relation = target->GetOptionRelation(ortCompilerOptions);

	std::list<wxString> prj_linker_opts;
	str_arr2list(project->GetLinkerOptions(), prj_linker_opts);
	std::list<wxString> target_linker_opts;
	str_arr2list(target->GetLinkerOptions(), target_linker_opts);

	switch(relation)
	{
		case orUseParentOptionsOnly:
			set_values(_T("QMAKE_LFLAGS +="), prj_linker_opts);
			break;

		case orUseTargetOptionsOnly:
			set_values(_T("QMAKE_LFLAGS +="), target_linker_opts);
			break;

		case orPrependToParentOptions:
			target_linker_opts.merge(prj_linker_opts);
			set_values(_T("QMAKE_LFLAGS +="), target_linker_opts);
			break;

		case orAppendToParentOptions:
			prj_linker_opts.merge(target_linker_opts);
			set_values(_T("QMAKE_LFLAGS +="), prj_linker_opts);
			break;
	}
}
//-------------------------------------------------
void QtPrjGen::fill_libs()
{
	OptionsRelation relation = target->GetOptionRelation(ortLinkerOptions);

	std::list<wxString> prj_libs;
	str_arr2list(project->GetLinkLibs(), prj_libs);
	std::list<wxString> target_libs;
	str_arr2list(target->GetLinkLibs(), target_libs);

	switch(relation)
	{
		case orUseParentOptionsOnly:
			check_libs(prj_libs);
			set_values(_T("LIBS +="), prj_libs);
			break;

		case orUseTargetOptionsOnly:
			check_libs(target_libs);
			set_values(_T("LIBS +="), target_libs);
			break;

		case orPrependToParentOptions:
			target_libs.merge(prj_libs);
			check_libs(target_libs);
			set_values(_T("LIBS +="), target_libs);
			break;

		case orAppendToParentOptions:
			prj_libs.merge(target_libs);
			check_libs(prj_libs);
			set_values(_T("LIBS +="), prj_libs);
			break;
	}
}
//-------------------------------------------------
void QtPrjGen::check_libs(std::list<wxString> &libs)
{
	//deleting 'lib' prefix and extention if required and add '-l' prefix
	wxString libPrefix = compiler->GetSwitches().libPrefix;
	wxString libExt = compiler->GetSwitches().libExtension;

	for(std::list<wxString>::iterator i = libs.begin(); i != libs.end(); ++i)
	{
		if(i->IsEmpty())
			continue;

		// construct linker option for each lib, based on compiler's settings
		mk_quoted_str(*i);
		// run replacements on libs only if no slashes in name (which means it's a relative or absolute path)
		if(i->Find('/') == -1 && i->Find('\\') == -1)
		{
			// 'lib' prefix
			bool hadLibPrefix = false;
			if(!compiler->GetSwitches().linkerNeedsLibPrefix &&
				!libPrefix.IsEmpty() &&
				i->StartsWith(libPrefix))
			{
				i->Remove(0, libPrefix.Length());
				hadLibPrefix = true;
			}

			// extension
			if(!compiler->GetSwitches().linkerNeedsLibExtension &&
				i->Length() > libExt.Length() &&
				i->Right(libExt.Length() + 1) == _T(".") + libExt)
			{
				// remove the extension only if we had a lib prefix
				if(hadLibPrefix)
					i->RemoveLast(libExt.Length() + 1);
			}
			else if(compiler->GetSwitches().linkerNeedsLibExtension && !libExt.IsEmpty())
			{
				if(i->Length() <= libExt.Length() ||
					i->Right(libExt.Length() + 1) != _T(".") + libExt)
				{
					*i << _T(".") << libExt;
				}
			}
			*i = compiler->GetSwitches().linkLibs + *i;
		}
		Manager::Get()->GetMacrosManager()->ReplaceEnvVars(*i);
	}
}
//-------------------------------------------------
void QtPrjGen::fill_lib_dirs()
{
	OptionsRelation relation = target->GetOptionRelation(ortLibDirs);

	std::list<wxString> prj_lib_dirs;
	str_arr2list(project->GetLibDirs(), prj_lib_dirs);
	std::list<wxString> target_lib_dirs;
	str_arr2list(target->GetLibDirs(), target_lib_dirs);

	switch(relation)
	{
		case orUseParentOptionsOnly:
			check_lib_dirs(prj_lib_dirs);
			set_values(_T("LIBS +="), prj_lib_dirs);
			break;

		case orUseTargetOptionsOnly:
			check_lib_dirs(target_lib_dirs);
			set_values(_T("LIBS +="), target_lib_dirs);
			break;

		case orPrependToParentOptions:
			target_lib_dirs.merge(prj_lib_dirs);
			check_lib_dirs(target_lib_dirs);
			set_values(_T("LIBS +="), target_lib_dirs);
			break;

		case orAppendToParentOptions:
			prj_lib_dirs.merge(target_lib_dirs);
			check_lib_dirs(prj_lib_dirs);
			set_values(_T("LIBS +="), prj_lib_dirs);
			break;
	}
}
//-------------------------------------------------
void QtPrjGen::check_lib_dirs(std::list<wxString> &lib_dirs)
{
	//adding '-L' prefix
	for(std::list<wxString>::iterator i = lib_dirs.begin(); i != lib_dirs.end(); ++i)
	{
		if(i->Trim().IsEmpty())
			continue;

		i->Replace(_T("\\"), _T("/"), true);
		Manager::Get()->GetMacrosManager()->ReplaceEnvVars(*i);
		*i = compiler->GetSwitches().libDirs + _T("\"") + *i + _T("\"");
	}

	lib_dirs.merge(get_values(_T("LIBS +=")));
}
//-------------------------------------------------
void QtPrjGen::fill_include_dirs()
{
	OptionsRelation relation = target->GetOptionRelation(ortIncludeDirs);

	std::list<wxString> prj_inc_dirs;
	str_arr2list(project->GetIncludeDirs(), prj_inc_dirs);
	std::list<wxString> target_inc_dirs;
	str_arr2list(target->GetIncludeDirs(), target_inc_dirs);

	switch(relation)
	{
		case orUseParentOptionsOnly:
			check_include_dirs(prj_inc_dirs);
			set_values(_T("INCLUDEPATH +="), prj_inc_dirs);
			break;

		case orUseTargetOptionsOnly:
			check_include_dirs(target_inc_dirs);
			set_values(_T("INCLUDEPATH +="), target_inc_dirs);
			break;

		case orPrependToParentOptions:
			target_inc_dirs.merge(prj_inc_dirs);
			check_include_dirs(target_inc_dirs);
			set_values(_T("INCLUDEPATH +="), target_inc_dirs);
			break;

		case orAppendToParentOptions:
			prj_inc_dirs.merge(target_inc_dirs);
			check_include_dirs(prj_inc_dirs);
			set_values(_T("INCLUDEPATH +="), prj_inc_dirs);
			break;
	}
}
//-------------------------------------------------
void QtPrjGen::check_include_dirs(std::list<wxString> &inc_dirs)
{
	for(std::list<wxString>::iterator i = inc_dirs.begin(); i != inc_dirs.end(); ++i)
	{
		if(i->IsEmpty())
			continue;

		i->Replace(_T("\\"), _T("/"), true);
		Manager::Get()->GetMacrosManager()->ReplaceEnvVars(*i);
		mk_quoted_str(*i);
	}
}
//-------------------------------------------------
void QtPrjGen::fill_prj_files()
{
	std::list<wxString> headers;
	std::list<wxString> sources;
	std::list<wxString> forms;
	std::list<wxString> resources;
	std::list<wxString> translations;
	std::list<wxString> pch;
	std::list<wxString> windres;

	int files_cnt = project->GetFilesCount();

	for(int i=0; i<files_cnt; ++i)
	{
		ProjectFile* file = project->GetFile(i);
		// if the file belongs in this target
		if(file->buildTargets.Index(target->GetTitle()) >= 0)
		{
			wxString fname = file->relativeFilename;
			// source files
			if(FileTypeOf(fname) == ftSource)
				sources.push_back(fname);
			// header files
			else if(FileTypeOf(fname) == ftHeader)
			{
				headers.push_back(fname);
				if(file->compile)
					pch.push_back(fname);
			}
			// designer form files
			else if(fname.AfterLast(_T('.')).Lower() == _T("ui"))
				forms.push_back(fname);
			// linguist translation files
			else if(fname.AfterLast(_T('.')).Lower() == _T("ts"))
				translations.push_back(fname);
			// Qt resource files
			else if(fname.AfterLast(_T('.')).Lower() == _T("qrc"))
				resources.push_back(fname);
			// Windows resource files
			else if(fname.AfterLast(_T('.')).Lower() == _T("rc"))
				windres.push_back(fname);
			}
		}

	set_values(_T("HEADERS ="), 			headers);
	set_values(_T("SOURCES ="), 			sources);
	set_values(_T("FORMS ="), 				forms);
	set_values(_T("RESOURCES ="), 			resources);
	set_values(_T("TRANSLATIONS ="), 		translations);
	set_values(_T("PRECOMPILED_HEADER ="), 	pch);
	set_values(_T("win32:RC_FILE ="),		windres);
}
//-------------------------------------------------
void QtPrjGen::fill_config()
{
	switch(target->GetTargetType())
	{
		case ttExecutable:
	#ifdef __WXMSW__
			add_value(_T("CONFIG +="), _T("windows"));
	#endif
			add_value(_T("TEMPLATE ="), _T("app"));
			break;

		case ttConsoleOnly:
			add_value(_T("CONFIG +="), _T("console"));
			add_value(_T("TEMPLATE ="), _T("app"));
			break;

		case ttStaticLib:
			add_value(_T("CONFIG +="), _T("staticlib"));
			add_value(_T("TEMPLATE ="), _T("lib"));
			break;

		case ttDynamicLib:
			add_value(_T("CONFIG +="), _T("dll"));
			add_value(_T("TEMPLATE ="), _T("lib"));
			break;

		case ttCommandsOnly:
		case ttNative:
			break;
	}

	wxString obj_dir = project->GetBasePath() + target->GetObjectOutput();
	add_value(_T("OBJECTS_DIR ="), mk_quoted_str(obj_dir));

	if(obj_dir.Last() != '/' && obj_dir.Last() != '\\')
		obj_dir += _T("/");

	wxString moc_dir = obj_dir + _T("moc");
	add_value(_T("MOC_DIR ="), mk_quoted_str(moc_dir));
	wxString uic_dir = obj_dir + _T("uic");
	add_value(_T("UI_DIR ="), mk_quoted_str(uic_dir));
	wxString rcc_dir = obj_dir + _T("rcc");
	add_value(_T("RCC_DIR ="), mk_quoted_str(rcc_dir));

	wxFileName out = target->GetOutputFilename();
	wxString dest_dir = out.GetPath(wxPATH_GET_VOLUME);
	if(dest_dir.IsEmpty())
		dest_dir = _T("./");

	add_value(_T("DESTDIR ="), mk_quoted_str(dest_dir));
	add_value(_T("TARGET ="), out.GetName());
}
//-------------------------------------------------
